#ifndef BVH_CUH
#define BVH_CUH



#include "hitable.cuh"
#include"sphere.cuh"
enum Axis { X, Y, Z };

__duel__ void swap(hitable* & p1, hitable* & p2) 
{
    hitable* temp = p1;
    p1 = p2;
    p2 = temp;
}
__duel__ void swap(sphere & p1, sphere & p2) 
{
    
    vec3 ce=p1.center;
    float ra=p1.radius;
    material*mat=p1.mat_ptr;
    p1 = p2;
    p2.center=ce;
    p2.mat_ptr=mat;
    p2.radius=ra;
}
__duel__ void swap(aabb & p1, aabb & p2) 
{
    
    aabb temp=p1;
    p1=p2;
    p2=temp;
}
template<Axis axis>
__host__ void bubble_sort(hitable** e, int n,aabb*host_e) 
{
    
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            aabb box_left, box_right;
            box_left = host_e[j];
            box_right =host_e[j+1];
            
            // ah->bounding_box(0, 0, box_left);
            // bh->bounding_box(0, 0, box_right);

            if ((axis == X && (box_left.min().x() - box_right.min().x()) < 0.0)
             || (axis == Y && (box_left.min().y() - box_right.min().y()) < 0.0)
             || (axis == Z && (box_left.min().z() - box_right.min().z()) < 0.0)) 
            {
                
                swap(e[j], e[j+1]);
                swap(host_e[j],host_e[j+1]);
                
            }
        }
    }
}

class Managed {
public:
  __duel__ void *operator new(size_t len) {
    void *ptr;
    cudaMallocManaged(&ptr, len);
    cudaDeviceSynchronize();
    return ptr;
  }

  __duel__ void operator delete(void *ptr) {
    cudaDeviceSynchronize();
    cudaFree(ptr);
  }
  __device__ virtual void dis(){};
};
class bvh_node: public hitable,public Managed
{
public:
    __host__ bvh_node(hitable **l, 
                       int n, 
                       float time0, 
                       float time1,
                       aabb*host_l
                       );
    __duel__ bvh_node(const bvh_node&bvh) 
    {
        this->left=nullptr;
        this->right=nullptr;
        this->length=0;
    }                  
    __duel__ virtual ~bvh_node();
    __device__ virtual bool hit(const ray& r, 
                                float t_min, 
                                float t_max, 
                                hit_record& rec) const;

    __duel__ virtual bool bounding_box(float t0, 
                                         float t1, 
                                         aabb& b) const;
    
    hitable* left;
    hitable* right;
    aabb box;
    int length;
};

__host__ bvh_node::~bvh_node() 
{
    
    if(this->length<=2)
    {
        return;
    }
    if(left)
    {
        delete left;
        left=nullptr;
    }
    if(right)
    {
        delete right;
        right=nullptr;
    }
    
};
bool funcflag=true;
__host__ bvh_node::bvh_node(hitable **l, 
                       int n, 
                       float time0, 
                       float time1,
                       aabb*host_l
                            )
{
    
    // if(funcflag)
    // {
            
            
            
    //         sphere s;
    //         uint64_t funcaddr = *reinterpret_cast<uint64_t*>(&s);
    //         for(int i=0;i<n;i++)
    //         {
    //             memcpy(host_l+i,&funcaddr,sizeof(uint64_t));
    //         }
            
    //         funcflag=false;
            
            
    // }
    
    int axis = int(3 * (rand()%2));
    if (axis == 0)
        bubble_sort<X>(l, n,host_l);
    else if (axis == 1)
        bubble_sort<Y>(l, n,host_l);
    else
        bubble_sort<Z>(l, n,host_l);
    
    
    
    length=n;
    
    
    

    if (n == 1) 
    {
        
        left = right = l[0];
        this->length=2;
    }else if (n == 2) 
    { 
        
        left  = l[0];
        right = l[1]; 
           
    }else 
    {
        
        left  = static_cast<hitable*>(new bvh_node(      l,     n/2, time0, time1,host_l));
        
        right = static_cast<hitable*>(new bvh_node(l + n/2, n - n/2, time0, time1,host_l+n/2));
        
    }
    
    aabb box_left, box_right;
    if(n==2)
    {
        box_left=host_l[0];
        box_right=host_l[1];
        // if (!.bounding_box(time0, time1, box_left) || 
        // !.bounding_box(time0, time1, box_right))
        // {
        //     return;
            
        // }
    }
    else if(n==1)
    {
        box_left=host_l[0];
        box_right=host_l[0];
        // if (!host_l[0].bounding_box(time0, time1, box_left) || 
        // !host_l[0].bounding_box(time0, time1, box_right))
        // {
        //     return;
            
        // }
    }
    else
    {
        if (!left->bounding_box(time0, time1, box_left) || 
        !right->bounding_box(time0, time1, box_right))
        {
            printf("here uncorect\n");
            return;
            
        }
    }
    
    box = surrounding_box(box_left, box_right);
    // if(n==2)
    // {
    //     printf("radius=%f ",host_l[0].radius);
    //     printf("%f\n",host_l[1].radius);
    //     printf("box_left\n");
    //     box_left.DisInfo();
    //     printf("box_right\n");
    //     box_right.DisInfo();
    //     printf("box\n");
    //     box.DisInfo();
    // }
    //

}


__duel__ bool bvh_node::bounding_box(float t0, 
                                      float t1, 
                                      aabb& b) const{
    b = box;
    return true;
}


__device__ bool bvh_node::hit(const ray& r, 
                             float t_min, 
                             float t_max, 
                             hit_record& rec) const
{
    //return false;
    float closest_so_far = t_max;
    bvh_node* stack[20];
    int ptr = 0;
	bvh_node* current = (bvh_node*)(this);
    bool ishitbal=false;
    
    do
    {
        if(current->length<=2)
        {

            hit_record left_rec, right_rec;
            bool hit_left  = current->left->hit(r, t_min, closest_so_far, left_rec);
            bool hit_right = current->right->hit(r, t_min, closest_so_far, right_rec); 
            if (hit_left && hit_right) 
            {
                
                if (left_rec.t < right_rec.t) 
                {
                    rec = left_rec;
                }
                else
                {
                    rec = right_rec;
                }
                ishitbal=true;
                closest_so_far=rec.t;
                
            }
            else if (hit_left)
            {
                rec = left_rec;
                ishitbal=true;
                closest_so_far=rec.t;
            }
            else if (hit_right)
            {
                rec = right_rec;
                ishitbal=true;
                closest_so_far=rec.t;
            }
            

            if (ptr <= 0){current = nullptr;break;}
			else{  current = stack[--ptr];}






            
            continue;
        }
        hitable * left = current->left;
		hitable * right = current->right;
        //hit_record left_rec;
        if(hitaabb(r, t_min, t_max,current->box))
        {
            
            current=(bvh_node*)left;
            stack[ptr++]= (bvh_node*)right;
        }
        else
        {
            if (ptr <= 0)current = nullptr;
			else  current = stack[--ptr];
        }
        
    } while (current!=nullptr);
    //free(stack);
    return ishitbal;
    
    
    
    
    

        
}

#endif  /* BHVNODE_H */